This document reproduces the different steps followed before generating the shiny app.

##Including relevant libraries

library(tidyverse)  # includes ggplot2 and readr
── Attaching core tidyverse packages ───────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.2     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.2     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(plotly)     # interactive plots

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

##Loading indices file

Mset <- read_delim(file="https://data.mbari.org/products/satellite-derived/global-modes/Mset.txt",comment="%",delim="\t")
Rows: 1363 Columns: 8── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): MONTH
dbl (7): YEAR, M1, M2, M3, M4, M5, M6
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Mset$time <- as.Date(paste(Mset$YEAR,Mset$MONTH,rep(15,length(Mset$YEAR))),format="%Y %m %d")
indices <- read_csv(file="https://data.mbari.org/products/satellite-derived/global-modes/climate_indices.csv",comment="%")
Rows: 865 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (11): year, month, MEI, PDO, NPGO, EMI, Nino3, Nino4, AMO, NAO, ATL3
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
nb_time <- length(indices$year)
indices$time <- as.Date(paste(indices$year,indices$month,rep(15,nb_time)),format="%Y %m %d")

##Plotting MEI & M1

plot(indices$time,indices$MEI,type="l",xlab="Time",ylab="Index")
points(Mset$time,Mset$M1,type="l",col="red")

Testing the use of varname & shading

indexname1 <- "PDO"
indexname2 <- "NPGO"
iok1 <- !is.na(indices[,indexname1])
ipos1 <- indices[,indexname1]>0 & !is.na(indices[,indexname1])
ineg1 <- indices[,indexname1]<0 & !is.na(indices[,indexname1])
x <- indices$time
y0 <- rep(0,nb_time)
plot(x,t(indices[,indexname1]),type="l",xlab="Time",ylab=indexname1)
polygon(c(x[iok1],rev(x[iok1])),c(y0[iok1],rev(t(indices[iok1,indexname1]))),col="skyblue")
points(x,t(indices[,indexname2]),type="l",col="red")

Testing 2 Y-axis

Based on https://www.r-bloggers.com/r-single-plot-with-two-different-y-axes/

indexname1 <- "M1"
indexname2 <- "MEI"
iok1 <- !is.na(Mset[,indexname1])
x <- Mset$time
y0 <- rep(0,length(Mset$YEAR))
par(mar = c(5,5,2,5))
plot(x,t(Mset[,indexname1]),type="l",xlab="Time",ylab=indexname1)
par(new=T)
with(indices,plot(time,MEI,type="l",axes=F,xlab=NA,ylab=NA,col="red",pch=16,cex=1.2))
#plot(indices$time,t(indices[,indexname2]),type="l",axes=F,xlab=NA, ylab=NA,col="red")
axis(side = 4, col="red")
mtext(side = 4, line = 3, indexname2,col="red")
legend("topleft",legend=c(indexname1,indexname2),lty=c(1,1),col=c("black", "red"))

Testing ggplot2 - area plot, double axis, interactive plot

indexname1 <- "M1"
indexname2 <- "MEI"
max1 <- max(Mset[, indexname1], na.rm = TRUE)
max2 <- max(indices[, indexname2], na.rm = TRUE)
Mset[, "varneg"] <- Mset[, indexname1]
Mset[, "varpos"] <- Mset[, indexname1]

Mset$varneg[which(Mset$varneg > 0)] <- rep(0, length(which(Mset$varneg > 0)))
Mset$varpos[which(Mset$varpos < 0)] <- rep(0, length(which(Mset$varpos < 0)))

commonxM <- Mset$time %in% indices$time
commonxI <- indices$time %in% Mset$time
subsetM <- Mset[commonxM, indexname1]
subsetI <- indices[commonxI, indexname2]
iok1 <- !is.na(subsetM) & !is.na(subsetI)
r = cor(subsetM[iok1,], subsetI[iok1, ])
legname <- paste(indexname2, " (r = ", toString(floor(r*100)/100), ")", sep = "")

p <- ggplot() +
  geom_area(data = Mset, aes(x = time, y = varneg), fill = "blue") +
  geom_area(data = Mset, aes(x = time, y = varpos), fill = "red") +
  geom_line(data = Mset, aes(x = time, y = eval(as.name(indexname1))), linewidth = 0) +
  geom_line(data = indices, aes(x = time, y = eval(as.name(indexname2))/(max2/max1), color = legname), linewidth=0.5) +
  scale_y_continuous(sec.axis = sec_axis(trans = ~.*(max2/max1), name = indexname2)) +
  scale_x_date(limits = c(min(Mset$time)-15, max(Mset$time)+15), expand = c(0, 0)) +
  labs(x = "Time", y = indexname1) +
  theme(legend.position = c(0.15, 0.95),legend.key.width = unit(3,"line"),legend.background = element_rect(fill="transparent"),
        legend.text = element_text(size = 11)) +
  scale_colour_manual("",
                      breaks = c(legname),
                      values = c("black"))
p2 <- plotly_build(p)
#style(p2) %>% layout(legend = list(x = 0.01, y = 1))   # needed because ggplotly removes the legend position
p2
LS0tCnRpdGxlOiAiQ2xpbWF0ZSBJbmRpY2VzIE5vdGVib29rIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpUaGlzIGRvY3VtZW50IHJlcHJvZHVjZXMgdGhlIGRpZmZlcmVudCBzdGVwcyBmb2xsb3dlZCBiZWZvcmUgZ2VuZXJhdGluZyB0aGUgc2hpbnkgYXBwLgoKIyNJbmNsdWRpbmcgcmVsZXZhbnQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpICAjIGluY2x1ZGVzIGdncGxvdDIgYW5kIHJlYWRyCmxpYnJhcnkocGxvdGx5KSAgICAgIyBpbnRlcmFjdGl2ZSBwbG90cwpgYGAKCiMjTG9hZGluZyBpbmRpY2VzIGZpbGUKYGBge3J9Ck1zZXQgPC0gcmVhZF9kZWxpbShmaWxlPSJodHRwczovL2RhdGEubWJhcmkub3JnL3Byb2R1Y3RzL3NhdGVsbGl0ZS1kZXJpdmVkL2dsb2JhbC1tb2Rlcy9Nc2V0LnR4dCIsY29tbWVudD0iJSIsZGVsaW09Ilx0IikKTXNldCR0aW1lIDwtIGFzLkRhdGUocGFzdGUoTXNldCRZRUFSLE1zZXQkTU9OVEgscmVwKDE1LGxlbmd0aChNc2V0JFlFQVIpKSksZm9ybWF0PSIlWSAlbSAlZCIpCmluZGljZXMgPC0gcmVhZF9jc3YoZmlsZT0iaHR0cHM6Ly9kYXRhLm1iYXJpLm9yZy9wcm9kdWN0cy9zYXRlbGxpdGUtZGVyaXZlZC9nbG9iYWwtbW9kZXMvY2xpbWF0ZV9pbmRpY2VzLmNzdiIsY29tbWVudD0iJSIpCm5iX3RpbWUgPC0gbGVuZ3RoKGluZGljZXMkeWVhcikKaW5kaWNlcyR0aW1lIDwtIGFzLkRhdGUocGFzdGUoaW5kaWNlcyR5ZWFyLGluZGljZXMkbW9udGgscmVwKDE1LG5iX3RpbWUpKSxmb3JtYXQ9IiVZICVtICVkIikKYGBgCgoKIyNQbG90dGluZyBNRUkgJiBNMQoKYGBge3J9CnBsb3QoaW5kaWNlcyR0aW1lLGluZGljZXMkTUVJLHR5cGU9ImwiLHhsYWI9IlRpbWUiLHlsYWI9IkluZGV4IikKcG9pbnRzKE1zZXQkdGltZSxNc2V0JE0xLHR5cGU9ImwiLGNvbD0icmVkIikKYGBgCgoKIyMgVGVzdGluZyB0aGUgdXNlIG9mIHZhcm5hbWUgJiBzaGFkaW5nCgpgYGB7cn0KaW5kZXhuYW1lMSA8LSAiUERPIgppbmRleG5hbWUyIDwtICJOUEdPIgppb2sxIDwtICFpcy5uYShpbmRpY2VzWyxpbmRleG5hbWUxXSkKaXBvczEgPC0gaW5kaWNlc1ssaW5kZXhuYW1lMV0+MCAmICFpcy5uYShpbmRpY2VzWyxpbmRleG5hbWUxXSkKaW5lZzEgPC0gaW5kaWNlc1ssaW5kZXhuYW1lMV08MCAmICFpcy5uYShpbmRpY2VzWyxpbmRleG5hbWUxXSkKeCA8LSBpbmRpY2VzJHRpbWUKeTAgPC0gcmVwKDAsbmJfdGltZSkKcGxvdCh4LHQoaW5kaWNlc1ssaW5kZXhuYW1lMV0pLHR5cGU9ImwiLHhsYWI9IlRpbWUiLHlsYWI9aW5kZXhuYW1lMSkKcG9seWdvbihjKHhbaW9rMV0scmV2KHhbaW9rMV0pKSxjKHkwW2lvazFdLHJldih0KGluZGljZXNbaW9rMSxpbmRleG5hbWUxXSkpKSxjb2w9InNreWJsdWUiKQpwb2ludHMoeCx0KGluZGljZXNbLGluZGV4bmFtZTJdKSx0eXBlPSJsIixjb2w9InJlZCIpCmBgYAoKIyMgVGVzdGluZyAyIFktYXhpcwoKQmFzZWQgb24gaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vci1zaW5nbGUtcGxvdC13aXRoLXR3by1kaWZmZXJlbnQteS1heGVzLyAKCmBgYHtyfQppbmRleG5hbWUxIDwtICJNMSIKaW5kZXhuYW1lMiA8LSAiTUVJIgppb2sxIDwtICFpcy5uYShNc2V0WyxpbmRleG5hbWUxXSkKeCA8LSBNc2V0JHRpbWUKeTAgPC0gcmVwKDAsbGVuZ3RoKE1zZXQkWUVBUikpCnBhcihtYXIgPSBjKDUsNSwyLDUpKQpwbG90KHgsdChNc2V0WyxpbmRleG5hbWUxXSksdHlwZT0ibCIseGxhYj0iVGltZSIseWxhYj1pbmRleG5hbWUxKQpwYXIobmV3PVQpCndpdGgoaW5kaWNlcyxwbG90KHRpbWUsTUVJLHR5cGU9ImwiLGF4ZXM9Rix4bGFiPU5BLHlsYWI9TkEsY29sPSJyZWQiLHBjaD0xNixjZXg9MS4yKSkKI3Bsb3QoaW5kaWNlcyR0aW1lLHQoaW5kaWNlc1ssaW5kZXhuYW1lMl0pLHR5cGU9ImwiLGF4ZXM9Rix4bGFiPU5BLCB5bGFiPU5BLGNvbD0icmVkIikKYXhpcyhzaWRlID0gNCwgY29sPSJyZWQiKQptdGV4dChzaWRlID0gNCwgbGluZSA9IDMsIGluZGV4bmFtZTIsY29sPSJyZWQiKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKGluZGV4bmFtZTEsaW5kZXhuYW1lMiksbHR5PWMoMSwxKSxjb2w9YygiYmxhY2siLCAicmVkIikpCmBgYAoKIyMgVGVzdGluZyBnZ3Bsb3QyIC0gYXJlYSBwbG90LCBkb3VibGUgYXhpcywgaW50ZXJhY3RpdmUgcGxvdAoKYGBge3J9CmluZGV4bmFtZTEgPC0gIk0xIgppbmRleG5hbWUyIDwtICJNRUkiCm1heDEgPC0gbWF4KE1zZXRbLCBpbmRleG5hbWUxXSwgbmEucm0gPSBUUlVFKQptYXgyIDwtIG1heChpbmRpY2VzWywgaW5kZXhuYW1lMl0sIG5hLnJtID0gVFJVRSkKTXNldFssICJ2YXJuZWciXSA8LSBNc2V0WywgaW5kZXhuYW1lMV0KTXNldFssICJ2YXJwb3MiXSA8LSBNc2V0WywgaW5kZXhuYW1lMV0KCk1zZXQkdmFybmVnW3doaWNoKE1zZXQkdmFybmVnID4gMCldIDwtIHJlcCgwLCBsZW5ndGgod2hpY2goTXNldCR2YXJuZWcgPiAwKSkpCk1zZXQkdmFycG9zW3doaWNoKE1zZXQkdmFycG9zIDwgMCldIDwtIHJlcCgwLCBsZW5ndGgod2hpY2goTXNldCR2YXJwb3MgPCAwKSkpCgpjb21tb254TSA8LSBNc2V0JHRpbWUgJWluJSBpbmRpY2VzJHRpbWUKY29tbW9ueEkgPC0gaW5kaWNlcyR0aW1lICVpbiUgTXNldCR0aW1lCnN1YnNldE0gPC0gTXNldFtjb21tb254TSwgaW5kZXhuYW1lMV0Kc3Vic2V0SSA8LSBpbmRpY2VzW2NvbW1vbnhJLCBpbmRleG5hbWUyXQppb2sxIDwtICFpcy5uYShzdWJzZXRNKSAmICFpcy5uYShzdWJzZXRJKQpyID0gY29yKHN1YnNldE1baW9rMSxdLCBzdWJzZXRJW2lvazEsIF0pCmxlZ25hbWUgPC0gcGFzdGUoaW5kZXhuYW1lMiwgIiAociA9ICIsIHRvU3RyaW5nKGZsb29yKHIqMTAwKS8xMDApLCAiKSIsIHNlcCA9ICIiKQoKcCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGRhdGEgPSBNc2V0LCBhZXMoeCA9IHRpbWUsIHkgPSB2YXJuZWcpLCBmaWxsID0gImJsdWUiKSArCiAgZ2VvbV9hcmVhKGRhdGEgPSBNc2V0LCBhZXMoeCA9IHRpbWUsIHkgPSB2YXJwb3MpLCBmaWxsID0gInJlZCIpICsKICBnZW9tX2xpbmUoZGF0YSA9IE1zZXQsIGFlcyh4ID0gdGltZSwgeSA9IGV2YWwoYXMubmFtZShpbmRleG5hbWUxKSkpLCBsaW5ld2lkdGggPSAwKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBpbmRpY2VzLCBhZXMoeCA9IHRpbWUsIHkgPSBldmFsKGFzLm5hbWUoaW5kZXhuYW1lMikpLyhtYXgyL21heDEpLCBjb2xvciA9IGxlZ25hbWUpLCBsaW5ld2lkdGg9MC41KSArCiAgc2NhbGVfeV9jb250aW51b3VzKHNlYy5heGlzID0gc2VjX2F4aXModHJhbnMgPSB+LioobWF4Mi9tYXgxKSwgbmFtZSA9IGluZGV4bmFtZTIpKSArCiAgc2NhbGVfeF9kYXRlKGxpbWl0cyA9IGMobWluKE1zZXQkdGltZSktMTUsIG1heChNc2V0JHRpbWUpKzE1KSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGxhYnMoeCA9ICJUaW1lIiwgeSA9IGluZGV4bmFtZTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMTUsIDAuOTUpLGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDMsImxpbmUiKSxsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJ0cmFuc3BhcmVudCIpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCIiLAogICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhsZWduYW1lKSwKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoImJsYWNrIikpCnAyIDwtIHBsb3RseV9idWlsZChwKQojc3R5bGUocDIpICU+JSBsYXlvdXQobGVnZW5kID0gbGlzdCh4ID0gMC4wMSwgeSA9IDEpKSAgICMgbmVlZGVkIGJlY2F1c2UgZ2dwbG90bHkgcmVtb3ZlcyB0aGUgbGVnZW5kIHBvc2l0aW9uCnAyCmBgYAoKCg==